﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting;
using WebAndLoadTestPluginsAndRules.Common;

namespace WebAndLoadTestPluginsAndRules.WebTestPlugins
{
    /// <summary>
    /// Appends query string value to each request
    /// </summary>
    [DisplayName("Append Query String Value")]
    [Description("Appends (or updates) a query string value for each request")]
    public class AppendQueryStringValue : WebTestPlugin
    {
        // Future ideas:
        // - Handle the databinding transparently (i.e. look for "{{" etc) - also create a ParameterValueResolver
        // - Add a parameter to control behaviour when context parameters don't exist

        private readonly IFilterFactory _filterFactory;

        public AppendQueryStringValue() : this(new FilterFactory())
        {
        }

        public AppendQueryStringValue(IFilterFactory filterFactory)
        {
            _filterFactory = filterFactory;
        }

        [DisplayName("Name")]
        [Description("The name of the query string parameter")]
        public string Name { get; set; }
        [DisplayName("Value")]
        [Description("The value to set the query string parameter to")]
        public string Value { get; set; }
        [DisplayName("Value Context Parameter")]
        [Description("The name of the context parameter to use for the query string parameter value")]
        public string ValueContextParameter { get; set; }
        [DisplayName("Overwrite if exists")]
        [Description("True to overwrite the value if it already exists, false to leave")]
        public bool OverwriteIfExists { get; set; }

        [Category("Filter")]
        [DisplayName("Filter")]
        [Description("Specifies a filter. The query string will only be added to urls matching the filter. Leave blank to apply to all requests")]
        public string FilterText { get; set; }
        [Category("Filter")]
        [DisplayName("Filter Method")]
        [Description("Specifies how to apply the Filter")]
        public FilterMethod FilterMethod { get; set; }
        [Category("Filter")]
        [DisplayName("Filter - Ignore Case")]
        [Description("True to perform case insensitive matching on the Filter")]
        public bool IgnoreCase { get; set; }


        private Filter _filter;
        protected Filter Filter
        {
            get
            {
                if (_filter == null)
                {
                    if (string.IsNullOrEmpty(FilterText))
                    {
                        _filter = FilterFactory.AlwaysTrue;
                    }
                    else
                    {
                        _filter = _filterFactory.GetFilter(FilterText, FilterMethod, IgnoreCase);
                    }
                }
                return _filter;
            }
        }

        public override void PreRequest(object sender, PreRequestEventArgs e)
        {
            WebTestRequest request = e.Request;
            WebTestContext context = e.WebTest.Context;
            
            PreRequest(request, context);
        }

        private void PreRequest(WebTestRequest request, IDictionary<string, object> context)
        {
            if (Filter.Matches(request.Url))
            {
                string value = GetParameterValue(context, ValueContextParameter, Value);
                request.QueryStringParameters.AddQueryStringParameter(Name, value, OverwriteIfExists);
            }
        }

        private static string GetParameterValue(IDictionary<string, object> webTestContext, string valueContextParameter, string parameterValue)
        {
            if (webTestContext == null)
            {
                throw new ArgumentNullException("webTestContext");
            }
            string value;
            if (string.IsNullOrEmpty(valueContextParameter))
            {
                value = parameterValue;
            }
            else
            {
                object valueTemp;
                if (!webTestContext.TryGetValue(valueContextParameter, out valueTemp))
                {
                    throw new WebTestException(string.Format("Context Parameter '{0}' was not found", valueContextParameter));
                }
                value = valueTemp == null ? "" : valueTemp.ToString();
            }
            return value;
        }
    }
}
